package com.zjw.zy.utils;

import android.annotation.SuppressLint;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Pair;

import androidx.annotation.NonNull;

import com.zjw.zy.constant.SpecialDay;
import com.zjw.zy.constant.WeatherConstant;
import com.zjw.zy.smi.ICopy;
import com.zjw.zy.smi.JustWithIt;
import com.zjw.zy.utils.outdepend.Lunar;
import com.zjw.zy.utils.outdepend.SolarTermUtils;
import com.zjw.zy.utils.outdepend.SunTimeUtils;

import org.jetbrains.annotations.NotNull;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Objects;
import java.util.TimeZone;

/**
 * @author ：zhong.jw
 * @date ：Created in 2022/9/27 18:30
 */
@SuppressLint("SimpleDateFormat")
public final class TimeUtils {

    //当前时间
    private final static TimeBean mTimeBean = new TimeBean();
    //昨日
    private final static TimeBean mLastTimeBean = new TimeBean();


    private final static TimeReceiver sReceiver = new TimeReceiver();

    //日出日落
    private final static SunTimeBean mSunTime = new SunTimeBean();

    private final static Object mLock = new Object();

    private final static HashSet<TimeListener> mObservers = new HashSet<>();

    private final static HashMap<Integer, List<Runnable>> mTimeScheduler = new HashMap<>();
    private final static HashMap<TimeBean, List<Runnable>> mDayScheduler = new HashMap<>();

    private static boolean isDay = true;

    private static volatile boolean isStart = false;

    private static final SimpleDateFormat sFormat = new SimpleDateFormat("yyyy/MM/dd");

    public static String formatDate(@NonNull Date date) {
        return sFormat.format(date);
    }

    public static String formatDate(@NonNull Date date, @NonNull String pattern) {
        return new SimpleDateFormat(pattern).format(date);
    }


    //添加定期任务
    public static void addDayScheduler(TimeBean timeBean, Runnable task) {
        synchronized (mDayScheduler) {
            List<Runnable> timeTaskList = mDayScheduler.computeIfAbsent(timeBean, k -> new ArrayList<>());
            timeTaskList.add(task);
        }
    }

    //添加定时任务
    public static void addTimeScheduler(int hour, int minute, @NonNull Runnable task) {
        synchronized (mTimeScheduler) {
            List<Runnable> timeTaskList = mTimeScheduler.computeIfAbsent(hour * 60 + minute, k -> new ArrayList<>());
            timeTaskList.add(task);
        }
    }

    //移除定时任务
    public static void removeTimeScheduler(int hour, int minute, @NonNull Runnable task) {
        synchronized (mTimeScheduler) {
            List<Runnable> timeTaskList = mTimeScheduler.get(hour * 60 + minute);
            if (timeTaskList != null) {
                timeTaskList.remove(task);
                if (timeTaskList.isEmpty()) {
                    mTimeScheduler.remove(hour * 60 + minute);
                }
            }
        }
    }

    public static void removeDayScheduler(@NonNull TimeBean timeBean, @NonNull Runnable task) {
        synchronized (mDayScheduler) {
            List<Runnable> timeTaskList = mDayScheduler.get(timeBean);
            if (timeTaskList != null) {
                timeTaskList.remove(task);
                if (timeTaskList.isEmpty()) {
                    mDayScheduler.remove(timeBean);
                }
            }
        }
    }

    static {
        //计算日出日落时间
        computeSunRiseTime();
    }

    private static void computeSunRiseTime() {
        Pair<Double, Double> location = WeatherConstant.WEATHER_CITY_LOCATION_MAP.get(SharePrefUtils.getInt(WeatherConstant.WEATHER_CURRENT_CITY_AREA_CODE_KEY,
                101040100));
        if (location != null) {
            Pair<String, String> todaySunTime = SunTimeUtils.getTodaySunTime(location.first, location.second);
            mSunTime.set(todaySunTime.first, todaySunTime.second);
            isDay = calcIsDay();
        }
    }

    public static Date currentDate() {
        return new Date();
    }

    public static TimeBean currentTime() {
        return mTimeBean.copy();
    }

    public static TimeBean getTimeBean(long date) {
        Calendar now = Calendar.getInstance(Locale.CHINA);
        now.setTime(new Date(date));
        return new TimeBean(now);
    }

    public static boolean isToday(@NonNull TimeBean date) {
        return mTimeBean.eqOfDay(date);
    }

    public static boolean isToday(long date) {
        TimeBean timeBean = TimeUtils.getTimeBean(date);
        return isToday(timeBean);
    }

    public static boolean isNow(@NonNull TimeBean date) {
        return mTimeBean.eqOfMillis(date);
    }

    public static boolean isFuture(@NonNull TimeBean date) {
        return date.mtOfMillis(mTimeBean);
    }

    public static boolean isPast(@NonNull TimeBean date) {
        return date.ltOfMillis(mTimeBean);
    }

    /**
     * 获得某一年中全部日期
     */
    @NonNull
    public static List<TimeBean> getTimeBeans(int year) {
        Calendar oneDay = Calendar.getInstance(Locale.CHINA);
        oneDay.clear();
        oneDay.set(year, 0, 1);
        int currentYears = year;
        List<TimeBean> beans = new ArrayList<>();
        while (currentYears == year) {
            beans.add(new TimeBean(oneDay));
            oneDay.add(Calendar.DAY_OF_MONTH, 1);
            currentYears = oneDay.get(Calendar.YEAR);
        }

        return beans;
    }

    /**
     * 获得某一月中全部日期
     */
    @NonNull
    public static List<TimeBean> getTimeBeans(int year, int month) {
        Calendar oneDay = Calendar.getInstance(Locale.CHINA);
        oneDay.clear();
        oneDay.set(year, month - 1, 1);
        int currentMonth = month;
        List<TimeBean> beans = new ArrayList<>();
        while (currentMonth == month) {
            beans.add(new TimeBean(oneDay));
            oneDay.add(Calendar.DAY_OF_MONTH, 1);
            currentMonth = oneDay.get(Calendar.MONTH) + 1;
        }

        return beans;
    }

    public static void onTimeChange() {
        synchronized (mLock) {
            Calendar calendar = Calendar.getInstance(Locale.CHINA);
            mTimeBean.updateTime(calendar);
        }
        boolean isDayTemp = calcIsDay();
        synchronized (mObservers) {
            for (TimeListener observer : mObservers) {
                observer.onTimeChange(mTimeBean.copy());

                if (isDayTemp != isDay) {
                    if (isDayTemp) {
                        observer.onArriveDay();
                    } else {
                        observer.onArriveNight();
                    }
                }
            }
            isDay = isDayTemp;
        }

        synchronized (mTimeScheduler) {
            //定时任务
            List<Runnable> timeTask = mTimeScheduler.get(mTimeBean.getHour() * 60 + mTimeBean.minute);
            if (timeTask != null) {
                for (Runnable task : timeTask) {
                    task.run();
                }
            }
        }
        synchronized (mDayScheduler) {
            TimeBean time = TimeUtils.mTimeBean.copy();
            List<Runnable> list = mDayScheduler.get(time);
            if (list != null) {
                for (Runnable task : list) {
                    task.run();
                }
                list.clear();
                mDayScheduler.remove(time);
            }
        }
    }

    private static boolean calcIsDay() {
        return mTimeBean.hour * 60 + mTimeBean.minute >= mSunTime.sunRaiseHour * 60 + mSunTime.sunRaiseMinute &&
                mTimeBean.hour * 60 + mTimeBean.minute < mSunTime.sunResetHour * 60 + mSunTime.sunResetMinute;
    }

    public static void registerTimeObserver(@NonNull TimeListener observer) {
        synchronized (mObservers) {
            observer.onTimeChange(mTimeBean);
            mObservers.add(observer);
        }
    }

    public static void unRegisterTimeObserver(@NonNull TimeListener observer) {
        synchronized (mObservers) {
            mObservers.remove(observer);
        }
    }

    /**
     * 是白天
     */
    public static boolean isDay() {
        return isDay;
    }

    @NotNull
    public static TimeBean createTimeBean(int add, int year, int month) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.clear();
        calendar.set(year, month - 1, 1);
        calendar.add(Calendar.MONTH, add);
        return new TimeBean(calendar);
    }

    @NotNull
    public static TimeBean createTimeBean(int add, int year, int month, int day) {
        Calendar calendar = Calendar.getInstance(Locale.CHINA);
        calendar.clear();
        calendar.set(year, month - 1, day);
        calendar.add(Calendar.DAY_OF_MONTH, add);
        return new TimeBean(calendar);
    }

    public static SunTimeBean getSunTime() {
        return mSunTime.copy();
    }

    /**
     * 解析时间为字符串
     *
     * @param currentDuration 毫秒
     */
    public static String parseToStrTime(int currentDuration) {
        int second = currentDuration / 1000 % 60;
        int minute = currentDuration / 1000 / 60;
        int hour = (currentDuration / 1000 / 60) % 60;
        String str = "";
        if (hour != 0) {
            str = adjustTime(hour) + ":";
        }
        str += adjustTime(minute) + ":" + adjustTime(second);
        return str;
    }

    public static TimeBean getTimeBean(int hour, int minute, int second) {
        TimeBean timeBean = new TimeBean();
        timeBean.updateTime(hour, minute, second);
        return timeBean;
    }

    /**
     * 判断是昨天
     */
    public static boolean isLastDay(long date) {
        Calendar today = Calendar.getInstance(Locale.CHINA);

        today.set(mTimeBean.year, mTimeBean.month - 1, mTimeBean.day, 0, 0, 0);

        long todayMillis = today.getTimeInMillis();

        long result = todayMillis - date;
        return result > 0 && result < 1000 * 60 * 60 * 24;
    }

    public interface TimeListener {
        void onTimeChange(@NonNull TimeBean date);

        default void onArriveDay() {
        }

        default void onArriveNight() {
        }
    }

    public final static class SunTimeBean implements ICopy<SunTimeBean> {
        private String sunRaiseStr;
        private String sunResetStr;
        private int sunRaiseHour;
        private int sunResetHour;
        private int sunRaiseMinute;
        private int sunResetMinute;


        public String getSunRaiseStr() {
            return sunRaiseStr;
        }

        public String getSunResetStr() {
            return sunResetStr;
        }

        public int getSunRaiseHour() {
            return sunRaiseHour;
        }

        public int getSunResetHour() {
            return sunResetHour;
        }

        public int getSunRaiseMinute() {
            return sunRaiseMinute;
        }

        public int getSunResetMinute() {
            return sunResetMinute;
        }

        private SunTimeBean() {

        }

        public SunTimeBean(@NonNull String sunRaiseStr, @NonNull String sunResetStr) {
            set(sunRaiseStr, sunResetStr);
        }

        @NonNull
        @Override
        public SunTimeBean copy() {
            return new SunTimeBean(sunRaiseStr, sunResetStr);
        }

        private void set(String sunRaiseStr, String sunResetStr) {
            this.sunRaiseStr = sunRaiseStr;
            this.sunResetStr = sunResetStr;
            String[] split1 = sunRaiseStr.split(":");
            String[] split2 = sunResetStr.split(":");
            sunRaiseHour = Integer.parseInt(split1[0]);
            sunRaiseMinute = Integer.parseInt(split1[1].trim());
            sunResetHour = Integer.parseInt(split2[0]);
            sunResetMinute = Integer.parseInt(split2[1].trim());
        }
    }

    public final static class TimeBean implements ICopy<TimeBean>, Comparable<TimeBean>, Parcelable {
        //当前年
        private int year;
        //当前月 [1~12]
        private int month;
        //当前日 [1~31]
        private int day;
        //当前时间 [0~23]
        private int hour;
        //[0~59]
        private int minute;
        //[0~59]
        private int second;
        //[0~6]星期天，星期一...星期六
        private int dayOfWeek;
        //时间序列
        private long currentTime;
        //农历
        private Lunar mLunar;
        //节气
        @SpecialDay.SolarTerm.SolarTermDes
        private String solar;

        protected TimeBean(Parcel in) {
            year = in.readInt();
            month = in.readInt();
            day = in.readInt();
            hour = in.readInt();
            minute = in.readInt();
            second = in.readInt();
            dayOfWeek = in.readInt();
            currentTime = in.readLong();
            mLunar = in.readParcelable(Lunar.class.getClassLoader());
            solar = in.readString();
        }

        public TimeBean(@NonNull Calendar calendar) {
            updateTime(calendar);
        }

        public TimeBean(@NonNull TimeBean other) {
            this.year = other.year;
            this.month = other.month;
            this.day = other.day;
            this.hour = other.hour;
            this.minute = other.minute;
            this.second = other.second;
            this.dayOfWeek = other.dayOfWeek;
            this.currentTime = other.currentTime;
            this.solar = other.solar;
            this.mLunar = other.mLunar;
        }

        public static final Creator<TimeBean> CREATOR = new Creator<TimeBean>() {
            @Override
            public TimeBean createFromParcel(Parcel in) {
                return new TimeBean(in);
            }

            @Override
            public TimeBean[] newArray(int size) {
                return new TimeBean[size];
            }
        };

        public int getYear() {
            return year;
        }

        public int getMonth() {
            return month;
        }

        public int getDay() {
            return day;
        }

        public int getHour() {
            return hour;
        }

        public int getMinute() {
            return minute;
        }

        public int getSecond() {
            return second;
        }

        public int getDayOfWeek() {
            return dayOfWeek;
        }

        public String getDayOfWeekStr() {
            switch (dayOfWeek) {
                case 0:
                    return "星期天";
                case 1:
                    return "星期一";
                case 2:
                    return "星期二";
                case 3:
                    return "星期三";
                case 4:
                    return "星期四";
                case 5:
                    return "星期五";
                case 6:
                    return "星期六";
            }
            return "";
        }

        public long getCurrentTime() {
            return currentTime;
        }

        public Lunar getLunar() {
            return mLunar;
        }

        @NonNull
        @SpecialDay.SolarTerm.SolarTermDes
        public String getSolar() {
            return solar;
        }

        public TimeBean() {

        }

        public boolean mtOfTime(@NonNull TimeBean other) {
            return this.hour > other.hour || this.minute > other.minute || this.second > other.second;
        }

        public boolean mtOfMillis(@NonNull TimeBean other) {
            return this.currentTime > other.currentTime;
        }

        public boolean eqOfMillis(@NonNull TimeBean other) {
            return this.currentTime == other.currentTime;
        }

        public boolean eqOfTime(@NonNull TimeBean other) {
            return this.hour == other.day && this.minute == other.minute && this.second == other.second;
        }

        public boolean ltOfTime(@NonNull TimeBean other) {
            return this.hour < other.hour || this.minute < other.minute || this.second < other.second;
        }

        public boolean ltOfMillis(@NonNull TimeBean other) {
            return this.currentTime < other.currentTime;
        }

        public boolean eqOfDay(@NonNull TimeBean other) {
            return this.year == other.year && this.month == other.month && this.day == other.day;
        }

        public void updateTime(int hour, int minute, int second) {
            this.hour = hour;
            this.minute = minute;
            this.second = second;

            updateMillion();
        }

        public void updateTime(@NonNull Calendar calendar) {
            this.year = calendar.get(Calendar.YEAR);
            this.month = calendar.get(Calendar.MONTH) + 1;
            this.day = calendar.get(Calendar.DAY_OF_MONTH);
            this.hour = calendar.get(Calendar.HOUR_OF_DAY);
            this.minute = calendar.get(Calendar.MINUTE);
            this.second = calendar.get(Calendar.SECOND);
            this.dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1;
            this.currentTime = calendar.getTimeInMillis();
            this.mLunar = new Lunar(calendar);
            this.solar = SolarTermUtils.getTermName(year, month, day);
        }

        @NonNull
        public String getStringOfYears() {
            return year + "/" + adjustTime(month) + "/" + adjustTime(day);
        }

        private void updateMillion() {
            Calendar calendar = Calendar.getInstance(Locale.CHINA);
            calendar.set(year, month - 1, day, hour, minute, second);

            this.currentTime = calendar.getTimeInMillis();
        }

        @NonNull
        public String getStringOfTime() {
            return adjustTime(hour) + ":" + adjustTime(minute);
        }

        public String getStringOfDay() {
            return adjustTime(month) + "/" + adjustTime(day);
        }

        @NonNull
        public String getSpecial() {
            String day = mLunar.toString();
            String special = SpecialDay.NULL;
            switch (day) {
                case "正月初一":
                    special = SpecialDay.SPRING_DAY;
                    break;
                case "正月十五":
                    special = SpecialDay.LANTERN_DAY;
                    break;
                case "五月初五":
                    special = SpecialDay.DRAGON_BOAT_DAY;
                    break;
                case "闰五月十九":
                case "五月十九":
                    special = SpecialDay.ZHONG_JI_WEI_BIRTHDAY;
                    break;
                case "七月初七":
                    special = SpecialDay.TANABATA_DAY;
                    break;
                case "八月十五":
                    special = SpecialDay.MID_AUTUMN_DAY;
                    break;
                case "十月初一":
                    special = SpecialDay.YANG_YANG_BIRTHDAY;
                    break;
            }

            day = getStringOfDay();
            switch (day) {
                case "01/01":
                    special += "|" + SpecialDay.NEW_YEARS_DAY;
                    break;
                case "02/14":
                    special += "|" + SpecialDay.VALENTINES_DAY;
                    break;
                case "05/01":
                    special += "|" + SpecialDay.LABOR_DAY;
                    break;
                case "10/01":
                    special += "|" + SpecialDay.NATIONAL_DAY;
                    break;
                case "12/09":
                    special += "|" + SpecialDay.LOVER_DAY;
                    break;
            }

            if (special.startsWith("|")) {
                special = special.substring(1);
            }

            if (!("".equals(solar))) {
                special += "|" + solar;

            }

            if (special.startsWith("|")) {
                special = special.substring(1);
            }

            return special;
        }


        @NonNull
        @Override
        public TimeBean copy() {
            return new TimeBean(this);
        }

        @Override
        public int compareTo(TimeBean o) {
            return (int) (this.currentTime - o.currentTime);
        }

        @Override
        public int describeContents() {
            return 0;
        }

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            dest.writeInt(year);
            dest.writeInt(month);
            dest.writeInt(day);
            dest.writeInt(hour);
            dest.writeInt(minute);
            dest.writeInt(second);
            dest.writeInt(dayOfWeek);
            dest.writeLong(currentTime);
            dest.writeString(solar);
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;
            TimeBean timeBean = (TimeBean) o;
            return year == timeBean.year && month == timeBean.month
                    && day == timeBean.day && hour == timeBean.hour
                    && minute == timeBean.minute && second == timeBean.second;
        }

        @Override
        public int hashCode() {
            return Objects.hash(year, month, day, hour, minute, second);
        }
    }


    public static String adjustTime(int number) {
        //补0
        if (number >= 10) {
            return "" + number;
        } else {
            return "0" + number;
        }
    }

    /**
     * 初始化相关
     */
    public static void start(@NonNull Context context) {
        if (isStart) {
            return;
        }
        isStart = true;
        IntentFilter filter = new IntentFilter();
        filter.addAction(Intent.ACTION_TIME_CHANGED);
        filter.addAction(Intent.ACTION_TIME_TICK);
        context.registerReceiver(sReceiver, filter);
        onTimeChange();
    }

    public static void stop(@NonNull Context context) {
        if (!isStart) {
            return;
        }
        isStart = false;
        context.unregisterReceiver(sReceiver);
    }


    private static class TimeReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
            if (intent != null) {
                switch (intent.getAction()) {
                    case Intent.ACTION_TIME_TICK:
                    case Intent.ACTION_TIME_CHANGED:
                        onTimeChange();
                        break;
                }
            }
        }
    }

}
